/**
 * @file    lv_indev_encoder.c
 * @author  Azolla (1228449928@qq.com)
 * @brief    
 * @version 0.1
 * @date    2024-04-15
 * 
 * @copyright Copyright (c) 2024
 * */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
 
#include "driver/pulse_cnt.h"
#include "driver/gpio.h"
#include "esp_log.h"

#include "lv_indev_encoder.h"

 
#define CONFIG_ENCODE_PINA            11    // A
#define CONFIG_ENCODE_PINB            12    // B
#define CONFIG_ENCODE_PINK            13    // KEY

static lv_indev_encoder_callback_t lv_indev_encoder_cb = NULL;

void lv_indev_encoder_register_callback(lv_indev_encoder_callback_t cb)
{
    lv_indev_encoder_cb = cb;
}
 
static int16_t encoder_diff = 0;
bool lv_indev_encoder_read(int16_t *enc_diff)
{
    *enc_diff = encoder_diff;
    return gpio_get_level(CONFIG_ENCODE_PINK);
}


 
static const char *TAG = "hal_pcnt";

#define CONFIG_PCNT_HIGH_LIMIT      400
#define CONFIG_PCNT_LOW_LIMIT      -400

#define CONFIG_PCNT_ENVENT_ENBALE   1

#if CONFIG_PCNT_ENVENT_ENBALE
static bool pcnt_on_reach(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)
{
    BaseType_t high_task_wakeup;
    QueueHandle_t queue = (QueueHandle_t)user_ctx;
    // send event data to queue, from this interrupt callback
    xQueueSendFromISR(queue, &(edata->watch_point_value), &high_task_wakeup);
    return (high_task_wakeup == pdTRUE);
}
#endif

typedef struct {
    QueueHandle_t queue;
    pcnt_unit_handle_t pcnt_unit;
} pcnt_task_t;
 
void pcnt_handler_task(void *arg)
{
    pcnt_task_t *task = (pcnt_task_t *)arg;
    // Report counter value
    int pulse_count = 0;
    int event_count = 0;
    while (true) {
        if (xQueueReceive(task->queue, &event_count, pdMS_TO_TICKS(CONFIG_LV_INDEV_DEF_READ_PERIOD))) {
            ESP_LOGI(TAG, "Watch point event, count: %d", event_count);
        } else {
            ESP_ERROR_CHECK(pcnt_unit_get_count(task->pcnt_unit, &pulse_count));
            //ESP_LOGI(TAG, "Pulse count: %d/%d", pulse_count, pulse_count/4);
            if (pulse_count < 0) pulse_count -= 2;
            else pulse_count += 2;
            encoder_diff = pulse_count / 4; // 测试EC11发现一个脉冲数对应4*pulse_count
            lv_indev_encoder_cb(encoder_diff);
        }
    }
}

void hal_pcnt_init(void)
{
    pcnt_unit_config_t unit_config = {
        .high_limit = CONFIG_PCNT_HIGH_LIMIT,
        .low_limit = CONFIG_PCNT_LOW_LIMIT,
    };
    pcnt_unit_handle_t pcnt_unit = NULL;
    ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));

    pcnt_glitch_filter_config_t filter_config = {
        .max_glitch_ns = 1000,
    };
    ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));

    pcnt_chan_config_t chan_a_config = {
        .edge_gpio_num = CONFIG_ENCODE_PINA,
        .level_gpio_num = CONFIG_ENCODE_PINB,
    };
    pcnt_channel_handle_t pcnt_chan_a = NULL;
    ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a));
    pcnt_chan_config_t chan_b_config = {
        .edge_gpio_num = CONFIG_ENCODE_PINB,
        .level_gpio_num = CONFIG_ENCODE_PINA,
    };
    pcnt_channel_handle_t pcnt_chan_b = NULL;
    ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_b_config, &pcnt_chan_b));

    ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
    ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
    ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
    ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));

#if CONFIG_PCNT_ENVENT_ENBALE
    int watch_points[] = {CONFIG_PCNT_LOW_LIMIT, -200, 0, 200, CONFIG_PCNT_HIGH_LIMIT};
    for (size_t i = 0; i < sizeof(watch_points) / sizeof(watch_points[0]); i++) {
        ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, watch_points[i]));
    }
    pcnt_event_callbacks_t cbs = {
        .on_reach = pcnt_on_reach,
    };
    QueueHandle_t queue = xQueueCreate(2, sizeof(int));
    ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &cbs, queue));
#endif
    ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
    ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
    ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
 
    static pcnt_task_t task;
    task.queue = queue;
    task.pcnt_unit = pcnt_unit;
    xTaskCreatePinnedToCore(pcnt_handler_task, "pcnt_handler_task", 4 * 1024, &task, 15, NULL, PRO_CPU_NUM);   
}

//  使用EC11旋转编码器时，可以在两个信号线上加个RC低通滤波，R:10K，C:0.01uf，效果会好很多，手册上就是这样的。
void lv_indev_encoder_init(void)
{
    /* Initialize PCNT event queue and PCNT functions */
    hal_pcnt_init(); 
 
    gpio_set_direction(CONFIG_ENCODE_PINK, GPIO_MODE_INPUT);
    gpio_set_pull_mode(CONFIG_ENCODE_PINK, GPIO_FLOATING);
}
